/*
 * Copyright 2014-2026 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.wzq.framework.util;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;

/**
 * 工具：日期处理<br>
 * pattern 必须存在且正确 (TIP: 为什么？调用者应该自己在做什么.)<br>
 * 时区：ll /usr/share/zoneinfo/
 * @author <a href="mailto:yanghao@01mi.net">杨浩</a>
 */
public class UtilDate {
	private static final Map<Object, ThreadLocal<DateFormat>> formatCache = new HashMap<>();
	public static final TimeZone tzShangHai = TimeZone.getTimeZone("Asia/Shanghai"),
			tzAmericaNewYork = TimeZone.getTimeZone("America/New_York");
	/** "yyyy-MM-dd HH:mm:ss.SSS" */
	public static final String defaultPattern = "yyyy-MM-dd HH:mm:ss.SSS";
	
	/**
	 * @param locale 地域(null使用当前)
	 * @param timeZone 时区(null使用当前)
	 * @param pattern 必填，调用者应该清楚自己在做什么
	 */
	public static DateFormat getDateFormat(Locale locale, TimeZone timeZone, String pattern) {
		if (timeZone == null) timeZone = TimeZone.getDefault();
		if (locale == null) locale = Locale.getDefault(Locale.Category.FORMAT);
		
		KeyComplex key = new KeyComplex(timeZone, locale, pattern);
		ThreadLocal<DateFormat> formatThreadLocal = formatCache.get(key);
		if (formatThreadLocal == null) {
			formatThreadLocal = new ThreadLocal<>();
			formatCache.put(key, formatThreadLocal);
		}
		
		DateFormat result = formatThreadLocal.get();
		if (result != null) return result;
		
		result = new SimpleDateFormat(pattern, locale);
		result.setTimeZone(timeZone);
		formatThreadLocal.set(result);
		return result;
	}
	
	public static Date parse(String date, Locale locale, TimeZone timeZone, String pattern) {
		if (date == null || date.isEmpty()) return null;
		
		if (pattern.length() > date.length()) pattern = pattern.substring(0, date.length());
		try {
			return getDateFormat(locale, timeZone, pattern).parse(date);
		} catch (ParseException e) {
			throw new IllegalArgumentException("pattern=" + pattern + ", date=" + date);
		}
	}
	public static String format(Date date, Locale locale, TimeZone timeZone, String pattern) {
		if (date == null) return null;
		return getDateFormat(locale, timeZone, pattern).format(date);
	}
	
	/** 使用当前locale和timezone转换 */
	public static Date parse(String date, String pattern) {
		return parse(date, null, null, pattern);
	}
	/** 使用当前locale和timezone转换, {@linkplain #defaultPattern} */
	public static Date parse(String date) {
		return parse(date, null, null, defaultPattern);
	}
	/** 使用当前locale和timezone格式化 */
	public static String format(Date date, String pattern) {
		return format(date, null, null, pattern);
	}
	/** 使用当前locale和timezone格式化, {@linkplain #defaultPattern}*/
	public static String format(Date date) {
		return format(date, null, null, defaultPattern);
	}
	/** 使用当前locale和timezone格式化, {@linkplain #defaultPattern}*/
	public static String format(long time) {
		return format(new Date(time), null, null, defaultPattern);
	}
	
	/** 转化为可读的持续时间格式 */
	public static String prettyTime(Number start, Number end) {
		if (start == null && end == null) return null;
		if (start == null) return "null --> " + format(new Date(end.longValue()), defaultPattern);
		if (end == null) return format(new Date(start.longValue()), defaultPattern) + " --> null";
		
		long time = end.longValue() - start.longValue();
		return time < 0 ? "-" + prettyTime(-time) : prettyTime(time);
	}
	/** 转化为可读的持续时间格式 */
	public static String prettyTime(Number detTime) {
		if (detTime == null) return null;
		
		long time = detTime.longValue();
		StringBuilder buf = new StringBuilder(128);
		int tmp = (int) (time / (24 * 3600 * 1000));
		if (tmp > 0) buf.append(tmp).append('天');
		time %= (24 * 3600 * 1000);
		if (time == 0) return buf.length() == 0 ? "0秒" : buf.toString();
		
		tmp = (int) (time / (3600 * 1000));
		if (tmp > 0) buf.append(tmp).append('时');
		time %= (3600 * 1000);
		if (time == 0) return buf.length() == 0 ? "0秒" : buf.toString();
		
		tmp = (int) (time / (60 * 1000));
		if (tmp > 0) buf.append(tmp).append('分');
		time %= (60 * 1000);
		if (time == 0) return buf.length() == 0 ? "0秒" : buf.toString();
		
		buf.append(time/1000f).append('秒');
		return buf.toString();
	}
	
	public static DateFormat getHttpDateFormat() {
		SimpleDateFormat format = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.UK);
		format.setTimeZone(TimeZone.getTimeZone("GMT"));
		return format;
	}
	public static Date parseHttpDate(String date) {
		try {
			return getHttpDateFormat().parse(date);
		} catch (ParseException e) {
			throw new IllegalArgumentException(date + ", " + e, e);
		}
	}
	public static String formatHttpDate(Date date) {
		return getHttpDateFormat().format(date);
	}
	
	public static Date getBeginOfDay(Date date) {
		return getBeginOfDay(date, false);
	}
	/**
     * 某日的开始
     * @param date
     * @param next true下一天，false当天
     * @return
     */
    public static Date getBeginOfDay(Date date, boolean next) {
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        c.set(Calendar.HOUR_OF_DAY, 0);
        c.set(Calendar.MINUTE, 0);
        c.set(Calendar.SECOND, 0);
        c.set(Calendar.MILLISECOND, 0);
        if (next) c.set(Calendar.DAY_OF_MONTH, c.get(Calendar.DAY_OF_MONTH) + 1);
        return c.getTime();
    }
    /**
     * 某日的开始
     * @param date 日期
     * @param next true下一天，false当天
     * @return [year, month(0), day(1), hour, min, second, millsecond]
     */
    public static int[] getAll(Date date) {
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        return new int[] {c.get(Calendar.YEAR), c.get(Calendar.MONTH), c.get(Calendar.DAY_OF_MONTH),
        		c.get(Calendar.HOUR_OF_DAY), c.get(Calendar.MINUTE), c.get(Calendar.SECOND),
        		c.get(Calendar.MILLISECOND)};
    }
}
